#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef _WIN32
#include <windows.h>
#include <windowsx.h>
#else
#include <sys/time.h>
#include <time.h>
#include "unistd.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"

#include <GL/gl.h>
#include "GL/glu.h"
#include <GL/glut.h>

#include "BList.h"
#include "BVector.h"
#include "geometrics.h"
#include "quaternion.h"
#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"

#include "AWLocalViewport.h"

#include "AWEntity.h"
#include "AWMessage.h"
#include "AWScenario.h"
#include "AWBody.h"
#include "AWSensor.h"
#include "AWBrain.h"
#include "AWAgent.h"
#include "AWWorld.h"

#include "loader.h"


AW_World::AW_World() : AW_Entity() 
{
	class_name->set("AW_World");
	shadows=false;
	scn=new AW_Scenario();
} /* AW_World::AW_World */ 


AW_World::AW_World(FILE *fp)
{
	class_name->set("AW_World");
	scn=0;
	load(fp);
} /* AW_World::AW_World */ 


AW_World::~AW_World() 
{
	delete scn;
} /* AW_World::AW_World */ 


bool AW_World::load(FILE *fp)
{
	int i,nLights,nAgents;
	Vector *light;
	AW_Agent *a;
	char tmp[80];

	if (!AW_Entity::load(fp)) return false;
	if (3!=fscanf(fp,"%f %f %f",&act_time,&time_rate,&max_timestep)) return false;
	
	if (scn!=0) delete scn;
	scn=(AW_Scenario *)ClassLoader(fp,0,0);

	if (1!=fscanf(fp,"%i",&nLights)) return false;
	for(i=0;i<nLights;i++) {
		light=new Vector(fp);
		light_sources.Add(light);
	} /* for */ 

	if (1!=fscanf(fp,"%s",tmp)) return false;
	shadows=false;
	if (strcmp(tmp,"Shadows")==0) shadows=true;

	if (!camera.load(fp)) return false;
	if (!camerap.load(fp)) return false;
	if (!initial_camera.load(fp)) return false;
	if (!initial_camerap.load(fp)) return false;

	if (1!=fscanf(fp,"%i",&nAgents)) return false;

	Agents.Delete();
	for(i=0;i<nAgents;i++) {
		a=(AW_Agent *)ClassLoader(fp,0,0);
		Agents.Add(a);
	} /* for */ 

	return true;
} /* AW_World::load */ 


bool AW_World::save(FILE *fp)
{
	AW_Agent *a;
	List<Vector> ll;
	Vector *light;

	if (!AW_Entity::save(fp)) return false;
	fprintf(fp,"%f %f %f\n",act_time,time_rate,max_timestep);

	if (!scn->save(fp)) return false;

	fprintf(fp,"%i\n",light_sources.Length());
	ll.Instance(light_sources);
	ll.Rewind();
	while(ll.Iterate(light)) light->save(fp);

	if (shadows) fprintf(fp,"Shadows\n");
			else fprintf(fp,"NoShadows\n");

	if (!camera.save(fp)) return false;
	if (!camerap.save(fp)) return false;
	if (!initial_camera.save(fp)) return false;
	if (!initial_camerap.save(fp)) return false;
		
	fprintf(fp,"%i\n",Agents.Length());

	Agents.Rewind();
	while(Agents.Iterate(a)) a->save(fp);

	return true;
} /* AW_World::save */ 


void AW_World::beforedraw(void)
{
	float tmpls[4]={1.0F,1.0F,1.0F,1.0F};
	float tmpld[4]={0.8F,0.8F,0.8F,1.0F};
	float tmpla[4]={0.2F,0.2F,0.2F,1.0F};
	float lightpos[4]={0,0,0,1.0F};
	int i;
	int lights[8]={GL_LIGHT0,GL_LIGHT1,GL_LIGHT2,GL_LIGHT3,GL_LIGHT4,GL_LIGHT5,GL_LIGHT6,GL_LIGHT7};

	/* Dibujar el mundo: */ 
	for(i=0;i<8;i++) glDisable(lights[i]);
	for(i=0;i<light_sources.Length();i++) {
		glEnable(lights[i]);
		glLightfv(lights[i],GL_AMBIENT,tmpla);
		glLightfv(lights[i],GL_DIFFUSE,tmpld);
		glLightfv(lights[i],GL_SPECULAR,tmpls);
		lightpos[0]=float(light_sources[i]->x);
		lightpos[1]=float(light_sources[i]->y);
		lightpos[2]=float(light_sources[i]->z);
		glLightfv(lights[i],GL_POSITION,lightpos);
	} /* for */ 

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	gluLookAt(camera.x,camera.y,camera.z,camerap.x,camerap.y,camerap.z,0,-1,0);
} /* AW_World::beforedraw */ 


void AW_World::afterdraw(void)
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
} /* AW_World::afterdraw */ 


void AW_World::draw(void)
{
	draw_world();
	if (shadows) draw_shadows();
} /* AW_World::draw */ 


void AW_World::draw_world(void)
{
	AW_Agent *a;

	scn->draw();
	Agents.Rewind();
	while(Agents.Iterate(a)) {
		a->draw();
	} /* while */ 
} /* AW_World::draw */ 


void AW_World::draw_shadows(void)
{
	int i;
	AW_Agent *a;
	float lightpos[4]={0,0,0,1.0F};

	for(i=0;i<light_sources.Length();i++) {
		glClear(GL_STENCIL_BUFFER_BIT);
		lightpos[0]=float(light_sources[i]->x);
		lightpos[1]=float(light_sources[i]->y);
		lightpos[2]=float(light_sources[i]->z);

		glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT );
		glDisable( GL_LIGHTING );
		glDepthMask( GL_FALSE );
		glDepthFunc( GL_LEQUAL );
		glEnable( GL_STENCIL_TEST );
		glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
		glStencilFunc( GL_ALWAYS, 1, 0xFFFFFFFFL );

		glEnable(GL_CULL_FACE);
		glFrontFace( GL_CCW );
		glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
		scn->draw_shadows(lightpos);
		Agents.Rewind();
		while(Agents.Iterate(a)) {
			a->draw_shadows(lightpos);
		} /* while */ 

		glFrontFace( GL_CW );
		glStencilOp( GL_KEEP, GL_KEEP, GL_DECR );
		scn->draw_shadows(lightpos);
		Agents.Rewind();
		while(Agents.Iterate(a)) {
			a->draw_shadows(lightpos);
		} /* while */ 

		glFrontFace( GL_CCW );
		glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );

		glDepthFunc(GL_ALWAYS);
		glDisable(GL_CULL_FACE);
		glColor4f(0.0,0.0,0.0,0.4f);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glStencilFunc(GL_NOTEQUAL,0,0xFFFFFFFFL);
		glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(-1.0, 1.0,0.0);
		glVertex3f(-1.0,-1.0,0.0);
		glVertex3f( 1.0, 1.0,0.0);
		glVertex3f( 1.0,-1.0,0.0);
		glEnd();
		glPopMatrix();
		glDisable(GL_BLEND);
		glPopAttrib();
	} /* for */ 

} /* AW_World::draw_shadows */ 



void AW_World::actualize(float time,float timeinc)
{
	AW_Agent *a;
	/* Actualizar el mundo: */ 
	act_time=time;

	actualizeagentlist();

	scn->actualize(time,timeinc);

	/* Actualizar los agentes: */ 
	Agents.Rewind();
	while(Agents.Iterate(a)) a->actualize(time,timeinc,this);

	actualizeagentlist();
} /* AW_World::actualize */ 


void AW_World::sendmessage(AW_Message *m)
{
	AW_Agent *a;

	Agents.Rewind();
	while(Agents.Iterate(a)) {
		m->time=act_time;
		if (a->get_AgentName()->cmp(m->receiver)) {
//			write_debug_report("AW_World: message delivered from %s to %s (size %i)\n",m->sender->get(),m->receiver->get(),strlen(m->content->get()));
			a->receive_msg(m);
		} /* if */ 
	} /* while */ 
} /* AW_World::sendmessage */ 


AW_Agent *AW_World::FindAgent(char *name)
{
	AW_Agent *a;

	Agents.Rewind();
	while(Agents.Iterate(a)) {
		if (a->get_AgentName()->cmp(name)) return a;
	} /* while */ 

	return 0;
} /* AW_World::FindAgent */ 


AW_Agent *AW_World::FindAgent(Symbol *name)
{
	AW_Agent *a;

	Agents.Rewind();
	while(Agents.Iterate(a)) {
		if (a->get_AgentName()->cmp(name)) return a;
	} /* while */ 

	return 0;
} /* AW_World::FindAgent */ 


bool AW_World::MouseClick(int,int)
{
	return false;
} /* AW_World::MouseClick */ 


bool AW_World::KeyboardEvent(int)
{
	return false;
} /* AW_World::KeyboardEvent */ 




void AW_World::addagent(AW_Agent *a)
{
	toadd.Add(a);
} /* AW_World::addagent */ 


void AW_World::killagent(AW_Agent *a)
{
	tokill.Add(a);
} /* AW_World::killagent */ 



void AW_World::actualizeagentlist(void)
{
	AW_Agent *a;

	while(!tokill.EmptyP()) {
		a=tokill.ExtractIni();
		Agents.DeleteElement(a);
		delete a;
	} /* while */ 

	while(!toadd.EmptyP()) {
		a=toadd.ExtractIni();
		Agents.Add(a);
	} /* while */ 
} /* AW_World::actualizeagentlist */ 


void AW_World::sense_agents(AW_Agent *agent,List<Symbol> *names,List<Vector> *poss,List<Vector> *dirs)
{
	AW_Agent *a;
	List<AW_Agent> al;

	al.Instance(Agents);
	al.Rewind();
	while(al.Iterate(a)) {
		if (a!=agent) {
			names->Add(new Symbol(a->get_AgentName()));
			poss->Add(new Vector(a->get_pos()));
			dirs->Add(new Vector(a->get_dir()));
		} /* if */ 
	} /* while */ 
} /* AW_World::sense_agent_names_and_pos */ 


bool AW_World::is_a(char *c)
{
	if (strcmp(c,"AW_World")==0) return true;

	return AW_Entity::is_a(c);
} /* AW_World::is_a */ 


bool AW_World::is_a(Symbol *c)
{
	if (c->cmp("AW_World")) return true;

	return AW_Entity::is_a(c);
} /* AW_World::is_a */ 


void AW_World::pause(void)
{
	AW_Agent *a;
	List<AW_Agent> al;

	al.Instance(Agents);
	al.Rewind();
	while(al.Iterate(a)) {
		a->pause();
	} /* while */ 
} /* AW_World::pause */ 


void AW_World::unpause(void)
{
	AW_Agent *a;
	List<AW_Agent> al;

	al.Instance(Agents);
	al.Rewind();
	while(al.Iterate(a)) {
		a->unpause();
	} /* while */ 
} /* AW_World::unpause */ 



List<Symbol> *AW_World::GetAgentNames(void)
{
	List<Symbol> *l=new List<Symbol>;
	AW_Agent *a;
	List<AW_Agent> al;

	al.Instance(Agents);
	al.Rewind();
	while(al.Iterate(a)) {
		l->Add(new Symbol(a->get_AgentName()));
	} /* while */ 

	return l;
} /* AW_World::GetAgentNames */ 


